/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package chessboard;

import com.sun.j3d.utils.picking.PickCanvas;
import com.sun.j3d.utils.picking.PickResult;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Node;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import Figures.*;
import java.util.ArrayList;
import javax.swing.JOptionPane;

/**
 *
 * @author jedrek
 */
public class MouseControler {

    private TransformGroup transformGroup;
    private ChessBoard chessBoard;
    FigureTransformGroup pickedFigure;
    Point pointedField;
    
    /**
     * if true then the user can control chess board (move view, select figures...) with mouse
     */
    public boolean mouseControllingEnabled = true;
    public int c=-1;
    private float x = 0;
    /**
     * czy przesywanie planszy w osi x jest ograniczone
     */
    public boolean xBounded = true;
    /**
     * ograniczenie przesuniecia planszy w osi x
     */
    public float minX = -3f;
    /**
     * ograniczenie przesuniecia planszy w osi x
     */
    public float maxX = 3f;

    private float y = 0;
    /**
     * czy przesywanie planszy w osi y jest ograniczone
     */
    public boolean yBounded = true;
    /**
     * ograniczenie przesuniecia planszy w osi y
     */
    public float minY = -3f;
    /**
     * ograniczenie przesuniecia planszy w osi y
     */
    public float maxY = 3f;

    private float z = 4f;
    /**
     * Po wywołaniu metody increaseZ(dz) zmienna z jest zwiększana o następującą wartość:
     * <br> zSpeedACoefficient*z + zSpeedBCoefficient*dz <br> lub <br>
     * -zSpeedACoefficient*z + zSpeedBCoefficient*dz <br> jeżeli dz mniejsze od 0
     */
    public float zSpeedACoefficient = 0.1f;
    /**
     * Po wywołaniu metody increaseZ(dz) zmienna z jest zwiększana o następującą wartość:
     * <br> zSpeedACoefficient*z + zSpeedBCoefficient*dz <br> lub <br>
     * -zSpeedACoefficient*z + zSpeedBCoefficient*dz <br> jeżeli dz mniejsze od 0
     */
    public float zSpeedBCoefficient = 0.2f;
    /**
     * czy oddalanie kamery jest ograniczone
     */
    public boolean zBounded = true;
    /**
     * Ograniczenia związane z odległością kamery
     */
    public float minZ = 3f;
    /**
     * Ograniczenia związane z odległością kamery
     */
    public float maxZ = 25f;

    private float alfa = 0.5f;
    /**
     * po wywolaniu metody increaseAlfa(dAlfa) kąt nachylenia zostanie zwiększony o alfaSpeedCoefficient*dAlfa
     */
    public float alfaSpeedCoefficient = 0.5f;
    /**
     * czy pochylanie kamery jest ograniczone
     */
    public boolean alfaBounded = true;
    /**
     * ograniczenie pochylenia kamery
     */
    public float minAlfa = 0.1f;
    /**
     * ograniczenie pochylenia kamery
     */
    public float maxAlfa = 1.5f;

    /**
     * kąt obrotu kamery
     */
    private float beta = 0f;
    /**
     * po wywolaniu metody increaseBeta(dBeta) kąt obrotu zostanie zwiększony o betaSpeedCoefficient*dBeta
     */
    public float betaSpeedCoefficient = 0.5f;
    /**
     * Ograniczenia związane z kątem obrotu kamery
     */
    public boolean betaBounded = false;
    /**
     * ograniczenie obrotu kamery
     */
    public float minBeta = -(float)Math.PI;
    /**
     * ograniczenie obrotu kamery
     */
    public float maxBeta = (float)Math.PI;


    /**
     * ustawia przesuniecie kamery w osi x
     * @param x przesniecie
     * @return true jezeli przesuniecie zostalo ustawione
     */
    public boolean setX(float x){
        if(xBounded){
            if(x<minX || x>maxX)
                return false;
            else{
                this.x = x;
            }
        } else
            this.x = x;
        update();
        return true;
    }

    /**
     * przesuwa kamere o zadana wartosc
     * @param dx o ile przesunac kamere
     * @return true jezeli kamera zostala przesunieta
     */
    public boolean increaseX(float dx){
        x+=dx;
        if(xBounded)
            if(x<minX || x>maxX){
                x-=dx;
                return false;
            }
        update();
        return true;
    }

    /**
     * ustawia przesuniecie kamery w osi y
     * @param y przesniecie
     * @return true jezeli przesuniecie zostalo ustawione
     */
    public boolean setY(float y){
        if(yBounded){
            if(y<minY || y>maxY)
                return false;
            else{
                this.y = y;
            }
        } else
            this.y = y;
        update();
        return true;
    }
 
    /**
     * przesuwa kamere o zadana wartosc
     * @param dy o ile przesunac kamere
     * @return true jezeli kamera zostala przesunieta
     */
    public boolean increaseY(float dy){
        y+=dy;
        if(yBounded)
            if(y<minY || y>maxY){
                y-=dy;
                return false;
            }
        update();
        return true;
    }

    /**
     * ustawia zadaną odległość kamery jeżeli nie łamie ona narzuconych ograniczeń
     * @param z nowa odległość kamery
     * @return true jeżeli nowa odległość została ustawiona, false w przeciwnym wypadku
     */
    public boolean setZ(float z){
        if(zBounded){
            if(z>minZ && z<maxZ)
                this.z = z;
            else
                return false;
        }
        else
            this.z = z;
        update();
        return true;
    }

    /**
     * Zwiększa odległość kamery jeżeli nie łamie to narzuconych ograniczeń
     * @param dz wartość, która zostanie dodana do aktualnej odległości kamery
     * @return true jeżeli odległość została zmieniona, false w przeciwnym wypadku
     */
    public boolean increaseZ(float dz){
        if(dz>0)
            dz = zSpeedACoefficient*z + zSpeedBCoefficient*dz;
        else
            dz = -zSpeedACoefficient*z + zSpeedBCoefficient*dz;
        z += dz;
        if(zBounded)
            if(z<minZ || z>maxZ){
                z -= dz;
                return false;
            }
        update();
        return true;
    }

    /**
     * ustawia zadany kąt nachylenia kamery jeżeli nie łamie on narzuconych ograniczeń
     * @param alfa nowy kąt nachylenia kamery
     * @return true jeżeli nowy kąt nachylenia został ustawiony, false w przeciwnym wypadku
     */
    public boolean setAlfa(float alfa){
        if(alfaBounded){
            if(alfa>minAlfa && alfa<maxAlfa)
                this.alfa = alfa;
            else
                return false;
        }
        else
            this.alfa = alfa;
        update();
        return true;
    }

    /**
     * Zwiększa kąt nachylenia kamery jeżeli nie łamie to narzuconych ograniczeń
     * @param dAlfa wartość, która zostanie dodana do aktualnego kąta nachylenia kamery
     * @return true jeżeli kąt nachylenia został zmieniony, false w przeciwnym wypadku
     */
    public boolean increaseAlfa(float dAlfa){
        alfa += alfaSpeedCoefficient*dAlfa;
        if(alfaBounded)
            if(alfa<minAlfa || alfa>maxAlfa){
                alfa -= alfaSpeedCoefficient*dAlfa;
                return false;
            }
        update();
        return true;
    }

    /**
     * ustawia zadany kąt obrotu kamery jeżeli nie łamie on narzuconych ograniczeń
     * @param beta nowy kąt obrotu kamery
     * @return true jeżeli nowy kąt obrotu został ustawiony, false w przeciwnym wypadku
     */
    public boolean setBeta(float beta){
        if(betaBounded){
            if(beta>minBeta && beta<maxBeta)
                this.beta = beta;
            else
                return false;
        }
        else
            this.beta = beta;
        update();
        return true;
    }

    /**
     * Zwiększa kąt obrotu kamery jeżeli nie łamie to narzuconych ograniczeń
     * @param dBeta wartość, która zostanie dodana do aktualnego kąta obrotu kamery
     * @return true jeżeli kąt obrotu został zmieniony, false w przeciwnym wypadku
     */
    public boolean increaseBeta(float dBeta){
        beta += betaSpeedCoefficient*dBeta;
        if(betaBounded)
            if(beta<minZ || beta>maxZ){
                beta -= betaSpeedCoefficient*dBeta;
                return false;
            }
        update();
        return true;
    }

    /**
     * odswieza polozenie kamery (ta metoda jest wywolywana automatycznie po wywolaniu metod zmieniajacych ustawienie kamery)
     */
    public void update(){
        Transform3D transform1 = new Transform3D();
        Transform3D transform2 = new Transform3D();

        transform1.set(new Vector3f(x, 0, y));
        transform2.rotY(-beta);
        transform1.mul(transform2);
        transform2.rotX(-alfa);
        transform1.mul(transform2);
        transform2.set(new Vector3f(0f, 0f, z));
        transform1.mul(transform2);
        transformGroup.setTransform(transform1);
    }

    /**
     * 
     * @param canvas plotno na ktorym bedzie wykrywana aktywnosc myszki
     * @param pickCanvas specjalne plotno sluzace do okreslania na ktory obiekt wskazuje kursor
     * @param transfromGroup wezel odpowiedzialny za polozenie kamery
     * @param chessBoard dla kogo odwalamy cala robote (parent)
     */
    public MouseControler(Canvas3D canvas, PickCanvas pickCanvas, TransformGroup transfromGroup, ChessBoard chessBoard){
        this.transformGroup = transfromGroup;
        this.chessBoard = chessBoard;
        MyMouseListener myMouseListener = new MyMouseListener(canvas, pickCanvas, this);
        canvas.addMouseListener(myMouseListener);
        canvas.addMouseMotionListener(myMouseListener);
        canvas.addMouseWheelListener(myMouseListener);
    }

    private class MyMouseListener implements MouseListener, MouseMotionListener, MouseWheelListener{

        private PickCanvas pickCanvas;
        private FigureTransformGroup pickedFigure;
        private Canvas3D canvas;
        private ArrayList<Point> possibleMoves;

        private boolean rightPressed = false;
        private boolean leftPressed = false;
        private boolean middlePressed = false;

        private int lastX;
        private int lastY;

        private MouseControler mouseControler;
        
        private Point enPassantVictim = null;
        private boolean castlingPossible = false;

        public MyMouseListener(Canvas3D canvas, PickCanvas pickCanvas, MouseControler mouseControler){
            this.canvas = canvas;
            this.mouseControler = mouseControler;
            this.pickCanvas = pickCanvas;
            possibleMoves = null;
        }

        public void mouseClicked(MouseEvent e) {
            if(!mouseControllingEnabled)
                return;
            if(e.getClickCount()==2){
                if(e.getButton()==MouseEvent.BUTTON1){
                }
                if(e.getButton()==MouseEvent.BUTTON2){
                    MouseControler.this.setX(0);
                    MouseControler.this.setY(0);
                }
                if(e.getButton()==MouseEvent.BUTTON3){
                    MouseControler.this.setAlfa(0.5f);
                    MouseControler.this.setBeta(0);
                    MouseControler.this.setZ(15);
                }
            }
        }

        public void mousePressed(MouseEvent e) {
            lastX = e.getX();
            lastY = e.getY();
            if(!mouseControllingEnabled)
                return;
            if(e.getButton()==MouseEvent.BUTTON1)
                leftPressed = true;
            else if(e.getButton()==MouseEvent.BUTTON3)
                rightPressed = true;
            else if(e.getButton()==MouseEvent.BUTTON2)
                middlePressed = true;
        }

        public void mouseReleased(MouseEvent e) {
            if(!mouseControllingEnabled)
                return;
            if(e.getButton()==MouseEvent.BUTTON1)
                leftPressed = false;
            else if(e.getButton()==MouseEvent.BUTTON3)
                rightPressed = false;
            else if(e.getButton()==MouseEvent.BUTTON2)
                middlePressed = false;

//            if(e.getButton() == MouseEvent.BUTTON3)
//                mouseControler.chessBoard.resetBoardState();

            if(mouseControler.chessBoard.isMoveBlocked())
                return;
            if(e.getClickCount()==1 && e.getButton() == MouseEvent.BUTTON1){
                //handle picking
                if(!mouseControler.chessBoard.playingEnable || mouseControler.chessBoard.whichColor == ChessBoard.COLOR_NONE)
                    return;
                FigureTransformGroup tmpFigure = pickFigure(e);
                if(tmpFigure != null)
                {
                    if(pickedFigure==null || pickedFigure.iColor == tmpFigure.iColor){
                        //if picked figure is not the color that is currently specified in ChessBoard then we return
                        if(mouseControler.chessBoard.whichColor != ChessBoard.COLOR_BOTH &&
                                mouseControler.chessBoard.whichColor!=tmpFigure.iColor)
                            return;
                        //we've chosen figure
                        createPossibleMoves(tmpFigure);
                        pickedFigure = tmpFigure;
                        mouseControler.chessBoard.highlightActive.setPosition(pickedFigure.pPosition);
                        mouseControler.chessBoard.highlightActive.show(true);
                        mouseControler.pickedFigure = pickedFigure;
                    }
                    else if(pickedFigure.iID!=tmpFigure.iID && isMovePossible(tmpFigure.pPosition)){
                        //we've made a move
                        mouseControler.chessBoard.highlightActive.show(false);
                        mouseControler.chessBoard.highlightAllowed.show(false);
                        mouseControler.chessBoard.highlightForbidden.show(false);
                        mouseControler.chessBoard.highlightBeating.show(false);
                        mouseControler.chessBoard.highlightCastlingBlue.show(false);
                        mouseControler.chessBoard.highlightCastlingGreen.show(false);
                        handleSpecialMove(pickedFigure.pPosition, tmpFigure.pPosition);
                        mouseControler.chessBoard.setUserMove(pickedFigure.pPosition, tmpFigure.pPosition, false);
                        pickedFigure = null;
                        mouseControler.pickedFigure = null;
                    }
                } else { //maybe we clicked on a chess board
                    Point p = mouseLocationToBoard(e.getX(), e.getY());

                    //did we clicked on a chessboard?
                    if(p.x>=0 && p.y>=0){
                        //is there a figure
                        if(mouseControler.chessBoard.iBoardState[p.x][p.y]>0)
                            tmpFigure = mouseControler.chessBoard.figures[mouseControler.chessBoard.iBoardState[p.x][p.y]-1];
                        //if no figure is picked and there is no figure on picked field then return
                        if(pickedFigure==null && tmpFigure==null)
                            return;
                        //we made a move to free field
                        if(tmpFigure==null){
                            if(isMovePossible(p)){
                                //we've made a move
                                mouseControler.chessBoard.highlightActive.show(false);
                                mouseControler.chessBoard.highlightAllowed.show(false);
                                mouseControler.chessBoard.highlightForbidden.show(false);
                                mouseControler.chessBoard.highlightBeating.show(false);
                                mouseControler.chessBoard.highlightCastlingBlue.show(false);
                                mouseControler.chessBoard.highlightCastlingGreen.show(false);
                                handleSpecialMove(pickedFigure.pPosition, p);
                                mouseControler.chessBoard.setUserMove(pickedFigure.pPosition, p, false);
                                pickedFigure = null;
                                mouseControler.pickedFigure = null;
                            }
                        }
                        //we picked (another) figure (tmpFigure must be!=null if we are here!)
                        else if(pickedFigure==null || pickedFigure.iColor == tmpFigure.iColor){
                            //is the color of the picked figure allowed?
                            if(mouseControler.chessBoard.whichColor != ChessBoard.COLOR_BOTH &&
                                    mouseControler.chessBoard.whichColor!=tmpFigure.iColor)
                                return;
                            //the color is allowed
                            createPossibleMoves(tmpFigure);
                            pickedFigure = tmpFigure;
                            mouseControler.pickedFigure = pickedFigure;
                            mouseControler.chessBoard.highlightActive.setPosition(pickedFigure.pPosition);
                            mouseControler.chessBoard.highlightActive.show(true);
                        } else if(isMovePossible(p)){
                            //we've made a move
                            mouseControler.chessBoard.highlightActive.show(false);
                            mouseControler.chessBoard.highlightAllowed.show(false);
                            mouseControler.chessBoard.highlightForbidden.show(false);
                            mouseControler.chessBoard.highlightBeating.show(false);
                            mouseControler.chessBoard.highlightCastlingBlue.show(false);
                            mouseControler.chessBoard.highlightCastlingGreen.show(false);
                            handleSpecialMove(pickedFigure.pPosition, tmpFigure.pPosition);
                            mouseControler.chessBoard.setUserMove(pickedFigure.pPosition, tmpFigure.pPosition, false);
                            pickedFigure = null;
                            mouseControler.pickedFigure = null;
                        }
                    }
                }
            }
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
//            mouseControler.chessBoard.highlightActive.show(false);
            mouseControler.chessBoard.highlightAllowed.show(false);
            mouseControler.chessBoard.highlightForbidden.show(false);
            mouseControler.chessBoard.highlightBeating.show(false);
            mouseControler.chessBoard.highlightCastlingBlue.show(false);
            mouseControler.chessBoard.highlightCastlingGreen.show(false);
        }

        public void mouseDragged(MouseEvent e) {
            float dx = e.getX() - lastX;
            float dy = e.getY() - lastY;
            lastY = e.getY();
            lastX = e.getX();
            if(!mouseControllingEnabled)
                return;
            if(leftPressed){
                mouseMoved(e);
                return;
            }
            if(rightPressed){
                mouseControler.increaseAlfa(0.01f*(dy));
                mouseControler.increaseBeta(0.01f*(dx));
            }
            if(middlePressed){
                float beta = MouseControler.this.beta;
                MouseControler.this.increaseX(-(float)(Math.cos(beta)*dx - Math.sin(beta)*dy)*0.008f);
                MouseControler.this.increaseY(-(float)(Math.cos(beta)*dy + Math.sin(beta)*dx)*0.008f);
            }
        }

        public void mouseMoved(MouseEvent e) {
            if(!mouseControllingEnabled)
                return;
            if(mouseControler.chessBoard.isMoveBlocked())
                return;
            //czy granie jest dozwolone
            if(!mouseControler.chessBoard.playingEnable || mouseControler.chessBoard.whichColor == ChessBoard.COLOR_NONE)
                return;
            FigureTransformGroup tmpFigure = null;
            Point p = null;
            try{
                tmpFigure = pickFigure(e);
            } catch(Exception ex) {return;}
            //moze wskazujemy na pole na ktorym stoi figura
            if(tmpFigure == null){
                p = mouseLocationToBoard(e.getX(), e.getY());
                if(p.x >= 0 && p.y >= 0)
                    if(mouseControler.chessBoard.iBoardState[p.x][p.y]>0)
                        tmpFigure = mouseControler.chessBoard.figures[mouseControler.chessBoard.iBoardState[p.x][p.y]-1];
            }
            if(tmpFigure != null){
                //kazda inna figure mozna wybrac jezeli juz jakas figura jest wybrana (bicie lub zmiana wyboru)
                if(pickedFigure!=null)
                {
                    if(isMovePossible(tmpFigure.pPosition) || pickedFigure.iColor==tmpFigure.iColor){
                        if(pickedFigure.iColor!=tmpFigure.iColor){
                            mouseControler.chessBoard.highlightBeating.setPosition(tmpFigure.pPosition);
                            mouseControler.chessBoard.highlightBeating.show(true);
                            mouseControler.chessBoard.highlightAllowed.show(false);
                            mouseControler.chessBoard.highlightForbidden.show(false);
                            mouseControler.chessBoard.highlightCastlingBlue.show(false);
                            mouseControler.chessBoard.highlightCastlingGreen.show(false);
                            mouseControler.pointedField = tmpFigure.pPosition;
                        } else{
                            mouseControler.chessBoard.highlightAllowed.setPosition(tmpFigure.pPosition);
                            mouseControler.chessBoard.highlightAllowed.show(true);
                            mouseControler.chessBoard.highlightBeating.show(false);
                            mouseControler.chessBoard.highlightForbidden.show(false);
                            mouseControler.chessBoard.highlightCastlingBlue.show(false);
                            mouseControler.chessBoard.highlightCastlingGreen.show(false);
                            mouseControler.pointedField = tmpFigure.pPosition;
                        }
                    }
                    else {
                        mouseControler.chessBoard.highlightAllowed.show(false);
                        mouseControler.chessBoard.highlightBeating.show(false);
                        mouseControler.chessBoard.highlightCastlingBlue.show(false);
                        mouseControler.chessBoard.highlightCastlingGreen.show(false);
                        mouseControler.chessBoard.highlightForbidden.setPosition(tmpFigure.pPosition);
                        mouseControler.chessBoard.highlightForbidden.show(true);
                        mouseControler.pointedField = tmpFigure.pPosition;
                    }
                }
                //czy wskazujemy na dozwolona figure
                else if(mouseControler.chessBoard.whichColor==ChessBoard.COLOR_BOTH || 
                        mouseControler.chessBoard.whichColor==tmpFigure.iColor){
                    mouseControler.chessBoard.highlightAllowed.setPosition(tmpFigure.pPosition);
                    mouseControler.chessBoard.highlightAllowed.show(true);
                    mouseControler.chessBoard.highlightForbidden.show(false);
                    mouseControler.chessBoard.highlightBeating.show(false);
                    mouseControler.chessBoard.highlightCastlingBlue.show(false);
                    mouseControler.chessBoard.highlightCastlingGreen.show(false);
                    mouseControler.pointedField = tmpFigure.pPosition;
                } else{
                    mouseControler.chessBoard.highlightAllowed.show(false);
                    mouseControler.chessBoard.highlightBeating.show(false);
                    mouseControler.chessBoard.highlightCastlingBlue.show(false);
                    mouseControler.chessBoard.highlightCastlingGreen.show(false);
                    mouseControler.chessBoard.highlightForbidden.show(true);
                    mouseControler.chessBoard.highlightForbidden.setPosition(tmpFigure.pPosition);
                    mouseControler.pointedField = tmpFigure.pPosition;
                }
            } else if(pickedFigure!=null){
                //czy wskazujemy na szachownice
                if(p.x >= 0 && p.y >= 0){
                    if(isMovePossible(p) || mouseControler.chessBoard.iBoardState[p.x][p.y]>0
                            && mouseControler.chessBoard.figures[mouseControler.chessBoard.iBoardState[p.x][p.y]-1].iColor == pickedFigure.iColor){
                        mouseControler.chessBoard.highlightAllowed.setPosition(p);
                        mouseControler.chessBoard.highlightAllowed.show(true);
                        mouseControler.chessBoard.highlightForbidden.show(false);
                        mouseControler.chessBoard.highlightBeating.show(false);
                        mouseControler.pointedField = p;
                        showSpecialMove(pickedFigure.pPosition, p);
                    }
                    else{
                        mouseControler.chessBoard.highlightAllowed.show(false);
                        mouseControler.chessBoard.highlightBeating.show(false);
                        mouseControler.chessBoard.highlightCastlingBlue.show(false);
                        mouseControler.chessBoard.highlightCastlingGreen.show(false);
                        mouseControler.chessBoard.highlightForbidden.setPosition(p);
                        mouseControler.chessBoard.highlightForbidden.show(true);
                        mouseControler.pointedField = p;
                    }
                }
                else{
                    mouseControler.chessBoard.highlightAllowed.show(false);
                    mouseControler.chessBoard.highlightForbidden.show(false);
                    mouseControler.chessBoard.highlightBeating.show(false);
                    mouseControler.chessBoard.highlightCastlingBlue.show(false);
                    mouseControler.chessBoard.highlightCastlingGreen.show(false);
                    mouseControler.pointedField = null;
                }
            } else {
                mouseControler.chessBoard.highlightAllowed.show(false);
                mouseControler.chessBoard.highlightForbidden.show(false);
                mouseControler.chessBoard.highlightBeating.show(false);
                mouseControler.chessBoard.highlightCastlingBlue.show(false);
                mouseControler.chessBoard.highlightCastlingGreen.show(false);
                if(p.x >= 0 && p.y >= 0)
                    mouseControler.pointedField = p;
                else
                    mouseControler.pointedField = null;
            }
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            if(!mouseControllingEnabled)
                return;
            mouseControler.increaseZ(0.1f*e.getWheelRotation());
        }
        
        /**
         * checks if a point specified in "e" points at a figure. If yes then the functions returns its FigureTransformGroup node
         * @param e mouse event containing cursor position
         * @return picked figure or null if no figure was picked
         */
        public FigureTransformGroup pickFigure(MouseEvent e)
        {
            FigureTransformGroup tmpPickedFigure = null;
            
            pickCanvas.setShapeLocation(e);
            PickResult result = pickCanvas.pickClosest();
            if(result!=null){   //we picked a figure
                Shape3D shape = (Shape3D)result.getNode(PickResult.SHAPE3D);
                if(shape!=null){
                    Node node = shape.getParent();
                    while(node!=null){
                        //looking for a Figure TransformGroup which is somewhere up in a graph (maybe a parent or grandfather or...)
                        try{
                            tmpPickedFigure = (FigureTransformGroup)node;
                            break;
                        } catch(ClassCastException ex){
                            node = node.getParent();
                        }
                    }
                    if(tmpPickedFigure!=null){
                        return tmpPickedFigure;
                    }
                }
            }
            return null;
        }
        
        /**
         * checks if given point is on the list of possible moves
         * @param point point to check
         * @return true if the point is on the list of possible moves
         */
        public boolean isMovePossible(Point point)
        {
            Point p;
            if(possibleMoves == null)
                return false;
            
            for(int i = 0; i < possibleMoves.size(); i++){
                p = possibleMoves.get(i);
                if(p.x == point.x && p.y == point.y)
                    return true;
            }
            
            return false;
        }
        
        /**
         * creates a list of possible moves for given figure. The list also contains special moves like en passant...
         * @param figure figure for which we want list of possible moves
         * @return true if the list was filled
         */
        public boolean createPossibleMoves(FigureTransformGroup figure){
            
            Figures moveChecker = null;
            boolean color;
            if(figure.iColor == FigureTransformGroup.WHITE)
                color = true;
            else
                color = false;
            
            switch(figure.iType)
            {
                case FigureTransformGroup.PAWN:
                    moveChecker = new Pawn(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y));
                    break;
                case FigureTransformGroup.ROOK:
                    moveChecker = new Rouge(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y));
                    break;
                case FigureTransformGroup.KNIGHT:
                    moveChecker = new Knight(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y));
                    break;
                case FigureTransformGroup.BISHOP:
                    moveChecker = new Bishop(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y));
                    break;
                case FigureTransformGroup.QUEEN:
                    moveChecker = new Queen(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y));
                    break;
                case FigureTransformGroup.KING:
                   moveChecker = new King(color, new Point(figure.pPosition.x, 7 - figure.pPosition.y), false, false);
                    break;
                default:
                    return false;
            }
            
            char[][] state = new char[8][8];
            mouseControler.chessBoard.getCharArrayReverse(state);
            possibleMoves = moveChecker.checkPossibleMoves(state);
            
            for(int i = 0; i<possibleMoves.size(); i++){
                possibleMoves.get(i).y = 7 - possibleMoves.get(i).y;
            }
            
            //en passant capture
            Point enPassant = getEnPassant(figure.pPosition.x, figure.pPosition.y);
            if(enPassant!=null){
                possibleMoves.add(enPassant);
                enPassantVictim = new Point(enPassant.x, figure.pPosition.y);
            } else{
                enPassant = null;
            }
            
            //castling
            if(getCastling(figure.pPosition.x, figure.pPosition.y)){
                castlingPossible = true;
            } else{
                castlingPossible = false;
            }
            
            return true;
        }

        /**
         * if move from "from" point to "to" point is some kind of special move then extra highlight 
         * squares may be displayed on the chessboard
         * @param from
         * @param to
         * @return always true
         */
        boolean showSpecialMove(Point from, Point to){
            //bicie w przelocie
            showEnPassant(from, to);
            showCastling(from, to);
            showPromotion(from, to);

            return true;
        }
        
        /**
         * does extra job if the move from "from" point to "to" point is a special move (like beating another pawn in en passant
         * @param from
         * @param to
         * @return always true
         */
        boolean handleSpecialMove(Point from, Point to){
            handleEnPassant(from, to);
            handleCastling(from, to);
            handlePromotion(from, to);
            
            return true;
        }
        
        /**
         * if move "from"->"to" is en passant then this method displays yellow square under pawn which is beaten
         * @param from
         * @param to
         * @return true if the square was shown (it is en passant), false if not
         */
        boolean showEnPassant(Point from, Point to){
            if(enPassantVictim != null && to.x == enPassantVictim.x && from.y == enPassantVictim.y){
                mouseControler.chessBoard.highlightBeating.setPosition(enPassantVictim);
                mouseControler.chessBoard.highlightBeating.show(true);
                return true;
            }
            return false;
        }
        
        /**
         * beats opponents's pawn if move "from"->"to" is en passant
         * @param from
         * @param to
         * @return true if the pawn was beaten, false otherwise
         */
        boolean handleEnPassant(Point from, Point to){
            if(enPassantVictim != null && to.x == enPassantVictim.x && from.y == enPassantVictim.y){
                mouseControler.chessBoard.beatFigure(enPassantVictim.x, enPassantVictim.y);
                return true;
            }
            
            return true;
        }
        
        /**
         * Checks if Pawn on position (x, y) can do En passant capture. If yes, then method returns point to which
         * the pawn will move
         * @param x position of the pawn - letters
         * @param y position of the pawn - numbers
         * @return Point, to which the pawn will move or null if there is no En passant possible for this pawn
         */
        Point getEnPassant(int x, int y){
            int ID = mouseControler.chessBoard.iBoardState[x][y];
            int ID2;
            if(ID <= 0)
                return null;
            
            FigureTransformGroup figure = mouseControler.chessBoard.figures[ID-1];
            FigureTransformGroup figure2;
            if(figure.iType != FigureTransformGroup.PAWN)
                return null;
            
            if(figure.iColor == FigureTransformGroup.WHITE && figure.pPosition.y != 4)
                return null;
            if(figure.iColor == FigureTransformGroup.BLACK && figure.pPosition.y != 3)
                return null;
            
            //on the left
            try{
                ID2 = mouseControler.chessBoard.iBoardState[x-1][y];
                if(ID2>0){  //czy jest potencjalna ofiara bicia w przelocie
                    figure2 = mouseControler.chessBoard.figures[ID2-1];
                    if(figure2.iType == FigureTransformGroup.PAWN){     //czy ofiara to pionek
                        if(figure2.wasMoved == 1){  //czy pionek wykonal tylko jeden ruch
                            if(figure2.iID == mouseControler.chessBoard.lastFigure.iID){    //czy ofiara wykonala ruch ostatnio
                                if(figure.iColor == FigureTransformGroup.WHITE)
                                    return new Point(x-1, y+1);
                                else
                                    return new Point(x-1, y-1);
                            }
                        }
                    }
                }
            } catch(Exception e){}
            
            //on the right
            try{
                ID2 = mouseControler.chessBoard.iBoardState[x+1][y];
                if(ID2>0){  //czy jest potencjalna ofiara bicia w przelocie
                    figure2 = mouseControler.chessBoard.figures[ID2-1];
                    if(figure2.iType == FigureTransformGroup.PAWN){     //czy ofiara to pionek
                        if(figure2.wasMoved == 1){  //czy pionek wykonal tylko jeden ruch
                            if(figure2.iID == mouseControler.chessBoard.lastFigure.iID){    //czy ofiara wykonala ruch ostatnio
                                if(figure.iColor == FigureTransformGroup.WHITE)
                                    return new Point(x+1, y+1);
                                else
                                    return new Point(x+1, y-1);
                            }
                        }
                    }
                }
            } catch(Exception e){}
            
            return null;
        }
        
        boolean showCastling(Point from, Point to){
            if(castlingPossible){
                if(to.x == 6){
                    mouseControler.chessBoard.highlightCastlingBlue.setPosition(7, from.y);
                    mouseControler.chessBoard.highlightCastlingBlue.show(true);
                    mouseControler.chessBoard.highlightCastlingGreen.setPosition(5, from.y);
                    mouseControler.chessBoard.highlightCastlingGreen.show(true);
                    return true;
                }
                if(to.x == 2){
                    mouseControler.chessBoard.highlightCastlingBlue.setPosition(0, from.y);
                    mouseControler.chessBoard.highlightCastlingBlue.show(true);
                    mouseControler.chessBoard.highlightCastlingGreen.setPosition(3, from.y);
                    mouseControler.chessBoard.highlightCastlingGreen.show(true);
                    return true;
                }
            }
            mouseControler.chessBoard.highlightCastlingBlue.show(false);
            mouseControler.chessBoard.highlightCastlingGreen.show(false);
            return false;
        }
        
        boolean handleCastling(Point from, Point to){
            if(castlingPossible){
                if(to.x == 6){
                    mouseControler.chessBoard.setUserMove(new Point(7, from.y), new Point(5, from.y), true);
                } else if(to.x == 2){
                    mouseControler.chessBoard.setUserMove(new Point(0, from.y), new Point(3, from.y), true);
                } else
                    return false;
                return true;
            }
            return false;
        }
        
        boolean getCastling(int x, int y){
            int ID = mouseControler.chessBoard.iBoardState[x][y];
            int ID2;
            char[][] boardState = new char[8][8];
            char k;
            boolean result = false;
            if(ID <= 0)
                return false;
            
            FigureTransformGroup figure = mouseControler.chessBoard.figures[ID-1];
            FigureTransformGroup figure2;
            if(figure.iType != FigureTransformGroup.KING)
                return false;
            if(figure.wasMoved > 0)
                return false;
            
            if(figure.iColor == FigureTransformGroup.WHITE)
                k = 'K';
            else
                k = 'k';
            
            //right (short)
            ID2 = mouseControler.chessBoard.iBoardState[7][y];
            if(ID2 > 0){    //czy tam cos stoi
                figure2 = mouseControler.chessBoard.figures[ID2-1];
                if(figure2.iType == FigureTransformGroup.ROOK && figure2.wasMoved == 0){    //czy to wczesniej nie ruszana wieza
                    mouseControler.chessBoard.getCharArray(boardState);
                    if(boardState[5][y] =='0' &&
                            boardState[6][y] == '0'){
                        if(!CheckChecker.isChecked(boardState, 4, y, k) &&   //czy wazne pola nie sa szachowane
                                !CheckChecker.isChecked(boardState, 5, y, k) && 
                                !CheckChecker.isChecked(boardState, 6, y, k)){
                            possibleMoves.add(new Point(6, y));
                            result = true;
                        }
                    }
                }
            }
            
            //left (long)
            ID2 = mouseControler.chessBoard.iBoardState[0][y];
            if(ID2 > 0){    //czy tam cos stoi
                figure2 = mouseControler.chessBoard.figures[ID2-1];
                if(figure2.iType == FigureTransformGroup.ROOK && figure2.wasMoved == 0){    //czy to wczesniej nie ruszana wieza
                    mouseControler.chessBoard.getCharArray(boardState);
                    if(boardState[3][y] =='0' &&
                            boardState[2][y] == '0' &&
                            boardState[1][y] == '0'){
                        if(!CheckChecker.isChecked(boardState, 4, y, k) &&   //czy wazne pola nie sa szachowane
                                !CheckChecker.isChecked(boardState, 3, y, k) && 
                                !CheckChecker.isChecked(boardState, 2, y, k)){
                            possibleMoves.add(new Point(2, y));
                            result = true;
                        }
                    }
                }
            }
            
            return result;
        }

        boolean showPromotion(Point from, Point to){
            return false;
        }

        boolean handlePromotion(Point from, Point to){
            int ID = mouseControler.chessBoard.iBoardState[from.x][from.y];
            int type = 0;
            if(ID <= 0)
                return false;

            FigureTransformGroup figure = mouseControler.chessBoard.figures[ID-1];
            if(figure.iType == FigureTransformGroup.PAWN){
                if(figure.iColor == FigureTransformGroup.WHITE && to.y != 7)
                    return false;
                if(figure.iColor == FigureTransformGroup.BLACK && to.y != 0)
                    return false;

                Object [] choices = {"Rook", "Knight", "Bishop", "Queen"};
                String choice = (String)JOptionPane.showInputDialog(null, null, "Choose figure", JOptionPane.INFORMATION_MESSAGE, null, choices, "Rook");
                
                if(choice == "Rook"){
                    type = FigureTransformGroup.ROOK;
                    c=1;
                }
                else if(choice == "Knight"){
                    type = FigureTransformGroup.KNIGHT;
                    c=2;
                }
                else if(choice == "Bishop"){
                    type = FigureTransformGroup.BISHOP;
                    c=3;
                }
                else if(choice == "Queen"){
                    type = FigureTransformGroup.QUEEN;
                    c=4;
                }
                else
                    return false;
                
                mouseControler.chessBoard.changePawn(ID, type);
                return true;
            }
            return false;
        }

        /**
         * calculates position on a chessboard from position of a canvas (for example mouse cursor)
         * @param x position on canvas
         * @param y position on canvas
         * @return position on a chessboard (location of a field)
         */
        public Point mouseLocationToBoard(int x, int y){
            //get two points needed for computing a ray (from mouse cursor to 3D world)
            Point3d a = new Point3d();
            canvas.getPixelLocationInImagePlate(x, y, a);

            Point3d b = new Point3d();
            canvas.getCenterEyeInImagePlate(b);

            Transform3D transform = new Transform3D();
            canvas.getImagePlateToVworld(transform);

            transform.transform(a);
            transform.transform(b);

            //now we use equation of a straight line to calculate if we clicked on a chessboard and where (we assume that y-cord = 0)
            double xBoard = -(-a.y*(b.x-a.x)/(b.y-a.y)+a.x-4);
            double zBoard = (-a.y*(b.z-a.z)/(b.y-a.y)+a.z+4);
            if(xBoard<0 || xBoard>8 || zBoard<0 || zBoard>8){
                xBoard = -1;
                zBoard = -1;
            }
            return new Point((int)xBoard, (int)zBoard);
        }
    }
}
